package de.uni_potsdam.hpi.bpt.ai.example;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.json.JSONException;
import org.springframework.util.StringUtils;

import de.uni_potsdam.hpi.bpt.ai.collection.Model;
import de.uni_potsdam.hpi.bpt.ai.diagram.Diagram;
import de.uni_potsdam.hpi.bpt.ai.diagram.Shape;
import de.uni_potsdam.hpi.bpt.ai.diagram.StencilSetFactory;
import de.uni_potsdam.hpi.bpt.ai.diagram.StencilType;
import de.uni_potsdam.hpi.bpt.ai.util.Aggregate;

public class GeneralStatistics extends Experiment {

	protected Map<String, Integer> namespaces = new HashMap<String, Integer>();
	protected Aggregate<Integer> revisions = new Aggregate<Integer>();
	protected Aggregate<Integer> numberOfUniqueConstructs = new Aggregate<Integer>();
	protected Map<String, Set<String>> unusedConstructs = new HashMap<String, Set<String>>();
	
	
	
	@Override
	protected void examineModel(Model model, Diagram diagram, int index) {
		
		// inspect name spaces
		String ns = diagram.getStencilset().getNamespace();
		if (null == ns) { 
			System.out.println("Invalid namespace: "+model.getId());
			return;
		}
		
		if (!this.namespaces.containsKey(ns)) {
			this.namespaces.put(ns, 1);
		}
		else {
			this.namespaces.put(ns, this.namespaces.get(ns) + 1);
		}
		
		// inspect revisions
		this.revisions.add(model.getNumberOfRevision());
		
		// which unused model elements?
		if (!this.unusedConstructs.containsKey(ns)) {
			this.unusedConstructs.put(ns, this.getVocabulary(ns));
		}
		
		// iterate over shapes in this model
		Set<String> constructs = new HashSet<String>();
		for (Shape shape : diagram.getShapes()) {
			
			// inspect number of unique constructs
			if (!constructs.contains(shape.getStencilId())) {
				constructs.add(shape.getStencilId());
			}
			
			// remove stencil from unused set of vocabulary
			this.unusedConstructs.get(ns).remove(shape.getStencilId());
		}
		
		// store number of uniqu constructs
		this.numberOfUniqueConstructs.add(constructs.size());
	}
	
	private Set<String> getVocabulary(String namespace) {
		try {
			return StencilSetFactory.getInstance().getStencilSet(namespace).getStencils();
		} catch (Exception e) {
			return new HashSet<String>();
		}
	}

	protected void printNamespaces(int modelCount) {

		System.out.println("\n"+String.format("  %76s  ", "NAMESPACES").replace(" ", "=")+"\n");
		
		List<String> keys = new ArrayList<String>(this.namespaces.keySet());
		final Map<String, Integer> finalNamspaces = new HashMap<String, Integer>(this.namespaces);
		Comparator<String> compare = new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return -1 * finalNamspaces.get(o1).compareTo(finalNamspaces.get(o2));
			}

		};
		Collections.sort(keys, compare);
		
		int maxL = 0;
		for (String l : keys) {
			maxL = Math.max(maxL, l.length()+1);
		}
		
		System.out.println(String.format("%"+maxL+"s + %-7d", "namespace", modelCount).replace(" ", "-"));
		for (String label : keys) {
			System.out.println(String.format("%"+maxL+"s | %-7d", label, finalNamspaces.get(label)));
		}
	}
	
	protected void printRevisions(int modelCount) {
		
		System.out.println("\n"+String.format("  %76s  ", "REVISIONS").replace(" ", "=")+"\n");
		
		final Map<Integer, Integer> finalRevisions = new HashMap<Integer, Integer>(this.revisions.cluster());
		List<Integer> keys = new ArrayList<Integer>(finalRevisions.keySet());
		Comparator<Integer> compare = new Comparator<Integer>(){
			@Override
			public int compare(Integer o1, Integer o2) {
				return -1 * finalRevisions.get(o1).compareTo(finalRevisions.get(o2));
			}
		};
		

		float[] ps = new float[]{0.9f, 0.8f, 0.75f, 0.5f, 0.25f, 0.2f, 0.1f};
		for (float p : ps) {
			//System.out.println((int)((1-p) * 100) + "% of the models have more or equal than " + Aggregate.r(this.revisions.quantile(p)) + " revisions");
			System.out.println("revisions: quantile("+p+")=" + Aggregate.r(this.revisions.quantile(p)));
		}
		
		System.out.println("The minimum number of revisions is " + this.revisions.min());
		System.out.println("The average number of revisions is " + Aggregate.r(this.revisions.avg()));
		System.out.println("The maximum number of revisions is " + this.revisions.max());
		
		System.out.println("\n Details follow... \n");
		System.out.println(String.format("%10s + %-5d", "#revisions", (int)(this.revisions.sum())).replace(" ","-"));
		for (Integer key : keys) {
			System.out.println(String.format("%10d | %-5d", key, finalRevisions.get(key)));
		}
		
	}
	
	protected void printNumberOfUniqueConstructs(int modelCount) {
		float[] ps = new float[]{0.9f, 0.8f, 0.75f, 0.5f, 0.25f, 0.2f, 0.1f};
		for (float p : ps) {
			//System.out.println((int)((1-p) * 100) + "% of the models have more or equal than " + Aggregate.r(this.numberOfUniqueConstructs.quantile(p)) + " unique constructs");
			System.out.println("numberOfUniqueConstructs: quantile("+p+")=" + Aggregate.r(this.numberOfUniqueConstructs.quantile(p)));
		}
		
		System.out.println("The minimum number of unique constructs is " + this.numberOfUniqueConstructs.min());
		System.out.println("The average number of unique constructs is " + Aggregate.r(this.numberOfUniqueConstructs.avg()));
		System.out.println("The maximum number of unique constructs is " + this.numberOfUniqueConstructs.max());
		
		System.out.println("\n Details follow .. \n");
		
		for (Entry<Integer, Integer> entry : this.numberOfUniqueConstructs.cluster().entrySet()) {
			System.out.println(entry.getKey() + ";" + entry.getValue());
		}
	}
	
	protected void printUnusedVocabulary(int modelCount) {
		for (Entry<String, Set<String>> vocab : this.unusedConstructs.entrySet()) {
			System.out.println(vocab.getKey() + " has " + vocab.getValue().size() + " unused constructs: ");
			System.out.println(StringUtils.collectionToCommaDelimitedString(vocab.getValue()));
			System.out.println();
		}
	}
	
	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, JSONException, IOException {
		
		for (String ns : Arrays.asList(new String[]{
				"http://b3mn.org/stencilset/epc#",
//				"http://b3mn.org/stencilset/petrinet#",
//				"http://b3mn.org/stencilset/bpmn1.1#",
//				"http://b3mn.org/stencilset/bpmn2.0#"
		})) {
			GeneralStatistics gs = new GeneralStatistics();
			int c = gs.eachModel(new HashSet<String>(Arrays.asList(new String[]{
				ns
			})));
			
			 gs.printNamespaces(c);
			 gs.printRevisions(c);
			 gs.printNumberOfUniqueConstructs(c);
			 gs.printUnusedVocabulary(c);
		}
		
		
		
	}
}
